﻿using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace DalamudManager
{
    class DownloadManager
    {
        static readonly ConcurrentQueue<DownloadTask> DLList = new ConcurrentQueue<DownloadTask>();
        static bool _running = false;

        public long TotalBytes { private set; get; } = 0;
        public long ReceivedBytes { private set; get; } = 0;
        public double Progress
        {
            get => TotalBytes != 0 ? (double)ReceivedBytes / TotalBytes : 0;
        }
        public string Target { private set; get; }

        public byte[] Result = null;

        public Exception Exception = null;

        public DownloadManager(string url)
        {
            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) =>
                {
                    return true;
                });
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
            }
            Target = url;
        }

        public bool DownloadFlag { private set; get; } = false;
        public bool ShutdownFlag { private set; get; } = false;

        public void Start(Action<byte[]> callback = null)
        {
            if (DownloadFlag || ShutdownFlag)
                return;
            DownloadFlag = true;
            DLList.Enqueue(new DownloadTask()
            {
                Manager = this,
                Callback = callback
            });
            if (!_running)
            {
                _running = true;
                var thread = new Thread(DownloadThread)
                {
                    IsBackground = true
                };
                thread.Start();
            }
        }

        public void Shutdown()
        {
            if (ShutdownFlag)
                return;
            ShutdownFlag = true;
        }

        static void DownloadThread()
        {
            while (true)
            {
                if (DLList.Count > 0)
                {
                    if (DLList.TryDequeue(out var task))
                    {
                        try
                        {
                            var wr = WebRequest.Create(task.Manager.Target);
                            wr.Timeout = 60000;
                            var response = wr.GetResponse();
                            task.Manager.TotalBytes = response.ContentLength;
                            Stream st = response.GetResponseStream();
                            Stream sd = new MemoryStream();
                            byte[] by = new byte[1024];
                            int osize = st.Read(by, 0, by.Length);
                            while (osize > 0 & !task.Manager.ShutdownFlag)
                            {
                                sd.Write(by, 0, osize);
                                task.Manager.ReceivedBytes += osize;
                                osize = st.Read(by, 0, by.Length);
                            }
                            if (!task.Manager.ShutdownFlag)
                            {
                                var bs = new byte[sd.Length];
                                sd.Seek(0, SeekOrigin.Begin);
                                sd.Read(bs, 0, (int)sd.Length);
                                task.Manager.ReceivedBytes = bs.Length;
                                task.Manager.Result = bs;
                                task.Callback?.Invoke(bs);
                            }
                            sd.Close();
                            st.Close();
                        }
                        catch (Exception ex)
                        {
                            task.Manager.Exception = ex;
                        }
                    }
                }
                else
                {
                    Thread.Sleep(500);
                }
            }
        }
    }

    class DownloadTask
    {
        public DownloadManager Manager;
        public Action<byte[]> Callback;
    }
}
